#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 

#define SIZE 512
#define ZERO '\0'
#define SKIP " "
#define NUM 32
#define SkipPath(p) do{ p += (strlen(p) + 1);while (*p != '/') p--; }while (0)
#define SkipSpace(usercommand, pos)  do{\
    while (1){\
        if (usercommand[pos] == ' ') pos++;\
        else break;\
    }\
}while (0)

#define None_REDIR   0
#define IN_REDIR     1
#define OUT_REDIR    2
#define APPEND_REDIR 3

int redir_type = None_REDIR;
char *filename = NULL;

char cwd[SIZE*2];
char pwd[SIZE*2];
char* gArgv[NUM];
int lastcode = 0;

const char* GetUserName()
{
    const char* name = getenv("USER");
    if (name == NULL) return "None";
    return name;
}

const char* GetHostName()
{
    const char* hostname = getenv("HOSTNAME");
    if (hostname == NULL) return "None";
    return hostname;
}

const char* GetCwd()
{
    const char* cwd = getenv("PWD");
    if (cwd == NULL)  return "None";
    return cwd;
}

void MakeCommandLineAndPrint()
{
    char line[SIZE];
    const char* username = GetUserName();
    const char* hostname = GetHostName();
    const char* cwd = GetCwd();
    SkipPath(cwd);
    snprintf(line, sizeof line,"[%s@%s %s]> ", username, hostname, strlen(cwd) == 1 ? "/" : cwd + 1);
    printf("%s", line);
    fflush(stdout);
}

int GetUserCommand(char usercommand[], size_t n)
{
    char* s = fgets(usercommand, n, stdin);
    if (s == NULL)  return -1;//暂时
    s[strlen(usercommand) - 1] = ZERO;

    return strlen(usercommand);
}

const char* GetHome()
{
    const char* home = getenv("HOME");
    if (home == NULL)
        return "/";
    return home;
}


void SplitCommand(char usercommand[], size_t n)
{
    (void)n;
    //"ls -a -l -n" => "ls" "-a" "-l" "-n"
    gArgv[0] = strtok(usercommand, SKIP);
    int index = 1;
    while ((gArgv[index++] = strtok(NULL, SKIP)));
}

void Die()
{

}

void ExecuteCommand()
{
    pid_t id = fork();
    if (id < 0) Die();
    else if (id == 0)
    {
        //重定向设置
        if (filename != NULL)
        {

            if (redir_type == IN_REDIR)
            {
                int fd = open(filename, O_RDONLY);
                dup2(fd, 0);
            }
            else if (redir_type == OUT_REDIR)
            {
                int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
                dup2(fd, 1);
            }
            else if (redir_type == APPEND_REDIR) 
            {
                int fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0666);
                dup2(fd, 1);
            }
            else 
            {

            }
        }

        //child
        execvp(gArgv[0], gArgv);
        exit(errno);
    }
    else 
    {
        //father
        int status = 0;
        pid_t rid = waitpid(id, &status, 0);
        if (rid > 0)
        {
            lastcode = WEXITSTATUS(status);
            if (lastcode) printf("%s:%s:%d\n", gArgv[0], strerror(lastcode), lastcode);
        }
    }
}

void Cd()
{
    const char* path = gArgv[1];
    if (path == NULL)
        path = GetHome();
    //path一定存在
    else if (strcmp(path, "~") == 0)
        path = GetHome();
    chdir(path);
    //刷新环境变量
    char temp[SIZE*2];
    if (strcmp(path, "-") == 0)
    {
        putenv(pwd);
        return;
    }
    else 
    {
        getcwd(temp, sizeof temp);
    }
    for (size_t i = 0; i < strlen(cwd); i++)
    {
        pwd[i] = cwd[i];
    }
    snprintf(cwd, sizeof cwd, "PWD=%s", temp);
    putenv(cwd);
}

int CheckBuildin()
{
    int yes = 0;
    const char* entercmd = gArgv[0];
    if (strcmp(entercmd, "cd") == 0)
    {
        yes = 1;
        Cd();
    }
    else if (strcmp(entercmd, "echo") == 0 && strcmp(gArgv[1], "$?") == 0)
    {
        yes = 1;
        printf("%d\n", lastcode);
        lastcode = 0;
    }

    return yes;
}

void CheckRedir(char usercommand[])
{
    int pos = 0;
    int end = strlen(usercommand);

    while (pos < end)
    {
        if (usercommand[pos] == '>')
        {
            if (usercommand[pos + 1] == '>')
            {
                usercommand[pos++] = 0;
                pos++;
                redir_type = APPEND_REDIR;
                SkipSpace(usercommand, pos);
                filename = usercommand + pos;
            }
            else 
            {
                usercommand[pos++] = 0;
                redir_type = OUT_REDIR;
                SkipSpace(usercommand, pos);
                filename = usercommand + pos;
            }
        }
        else if (usercommand[pos] == '<')
        {
            usercommand[pos++] = 0;
            redir_type = IN_REDIR;
            SkipSpace(usercommand, pos);
            filename = usercommand + pos;
        }
        else 
        {
            pos++;
        }
    }
}
int main()
{
    int quit = 0;
    while (!quit)
    {
        redir_type = None_REDIR;
        filename = NULL;
        //1.自己先输出一个命令行
        MakeCommandLineAndPrint();

        //2.获取用户命令字符串
        char usercommand[SIZE];
        int n = GetUserCommand(usercommand, sizeof usercommand);
        if (n <= 0) return 1;
        
        //2.1 checkredir
        CheckRedir(usercommand);

        //3.命令行字符串分割
        SplitCommand(usercommand, sizeof usercommand);
        //for (int i = 0; gArgv[i]; i++)
        //{
        //    printf("gArgv[%d]:%s\n", i, gArgv[i]);
        //}
        
        //4.优先执行内建命令
        n = CheckBuildin();
        if (n)  continue;
        //5.执行命令
        ExecuteCommand();
    }

    return 0;
}
